﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace GotLyrics
{
    public class UltraGrabber
    {
        private bool _isDirectory;
        /// <summary>
        /// Indicates if <see cref="Filename"/> is a directory.
        /// </summary>
        public bool IsDirectory
        {
            get { return _isDirectory; }
        }

        /*private bool _skipFilesWithLyrics;
        /// <summary>
        /// Gets or sets a value indicating whether to skip files that already have lyrics.
        /// </summary>
        /// <value>
        /// 	<c>true</c> to skip files with lyrics; otherwise, <c>false</c>.
        /// </value>
        public bool SkipFilesWithLyrics
        {
            get { return _skipFilesWithLyrics; }
            set { _skipFilesWithLyrics = value; }
        }*/

        private string _filename;
        /// <summary>
        /// Gets or sets the filename or starting directory.
        /// </summary>
        /// <value>The filename or starting directory.</value>
        public string Filename
        {
            get { return _filename; }
            set { _filename = value; }
        }

        private bool _recursive;
        /// <summary>
        /// Gets or sets a value indicating whether to check for files recursively.
        /// </summary>
        /// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
        public bool Recursive
        {
            get { return _recursive; }
            set { _recursive = value; }
        }

        private ILyricGrabber[] _lyricGrabbers;
        /// <summary>
        /// Gets the lyric grabber.
        /// </summary>
        /// <value>The lyric grabber.</value>
        public ILyricGrabber[] LyricGrabbers
        {
            get
            {
                if (_lyricGrabbers == null)
                {
                    _lyricGrabbers = Common.GetLyricGrabbers();
                }

                return _lyricGrabbers;
            }
        }

        /// <summary>
        /// Executes this instance.
        /// </summary>
        public void Execute()
        {
            Validate();

            if (_isDirectory)
                OnScanDirectory(_filename);
            else
                OnProcessFile(_filename);
        }

        /// <summary>
        /// Occurs when a directory scan has started.
        /// </summary>
        public event EventHandler<ScanDirectoryEventArgs> ScanDirectory;

        /// <summary>
        /// Occurs when an error is detected when processing a file.
        /// </summary>
        public event EventHandler<ProcessFileErrorEventArgs> ProcessFileError;

        /// <summary>
        /// Called when scanning a directory for files.
        /// </summary>
        /// <param name="directory">The directory.</param>
        protected virtual void OnScanDirectory(string directory)
        {
            if(ScanDirectory != null)
                ScanDirectory(this, new ScanDirectoryEventArgs(directory));

            string[] files = Directory.GetFiles(directory, "*.mp3");

            foreach (string file in files)
            {
                try
                {
                    OnProcessFile(file);
                }
                catch (Exception ex)
                {
                    
                    if (ProcessFileError != null)
                    {
                        ProcessFileErrorEventArgs args = new ProcessFileErrorEventArgs(file, ex);

                        ProcessFileError(this, args);

                        if (args.Continue == true)
                            continue;
                    }
                    
                    throw (new ApplicationException("Error occured while processing file \"" + file + "\"", ex));
                }
            }

            if (_recursive)
            {
                string[] folders = Directory.GetDirectories(directory);

                foreach (string folder in folders)
                    OnScanDirectory(folder);
            }
        }

        /// <summary>
        /// Occurs when a new file is beeing processed.
        /// </summary>
        public event EventHandler<ProcessFileEventArgs> ProcessFileBegin;

        /// <summary>
        /// Occurs when a file has been processed.
        /// </summary>
        public event EventHandler<ProcessFileEventArgs> ProcessFileEnd;

        /// <summary>
        /// Occurs when executing grabber.
        /// </summary>
        public event EventHandler<ExecutingGrabberEventArgs> ExecutingGrabber;

        /// <summary>
        /// Occurs when a grabber has found lyrics.
        /// </summary>
        public event EventHandler<LyricsFoundEventArgs> LyricsFound;

        /// <summary>
        /// Occurs when one of the lyric grabbers throws an exception.
        /// </summary>
        public event EventHandler<GrabberErrorEventArgs> GrabberError;

        /// <summary>
        /// Called for each file to be processed.
        /// </summary>
        /// <param name="file">The file.</param>
        protected virtual void OnProcessFile(string filename)
        {
            LyricGrabberContext context = new LyricGrabberContext(filename);

            ProcessFileEventArgs args = new ProcessFileEventArgs(context);

            if (ProcessFileBegin != null)
                ProcessFileBegin(this, args);

            if (args.SkipFile)
                return;

            foreach (ILyricGrabber grabber in LyricGrabbers)
            {
                if (ExecutingGrabber != null)
                    ExecutingGrabber(this, new ExecutingGrabberEventArgs(context, grabber));

                if (grabber.Enabled == false)
                    continue;

                string lyrics = null;

                try
                {
                    lyrics = grabber.Execute(context);
                }
                catch (Exception ex)
                {
                    if (GrabberError != null)
                    {
                        GrabberErrorEventArgs grabberErrorArgs = new GrabberErrorEventArgs(context, grabber, ex);

                        GrabberError(this, grabberErrorArgs);

                        if (grabberErrorArgs.Continue)
                            continue;
                    }

                    throw (new ApplicationException("Error occured while grabbing lyric with " + grabber.Name + " for \"" + filename + "\"", ex));
                }

                if (!string.IsNullOrEmpty(lyrics))
                {
                    context.Lyrics.Add(new LyricResult(lyrics, grabber));

                    LyricsFoundEventArgs lyricsFoundArgs = new LyricsFoundEventArgs(context, grabber, lyrics);

                    if (LyricsFound != null)
                        LyricsFound(this, lyricsFoundArgs);

                    if (!lyricsFoundArgs.Continue)
                        break;
                }
            }

            if (ProcessFileEnd != null)
                ProcessFileEnd(this, args);
        }

        private void Validate()
        {
            if (string.IsNullOrEmpty(Filename))
                throw (new ValidationException("No filename or starting directory given."));

            _isDirectory = Directory.Exists(_filename);

            if (!_isDirectory && !File.Exists(_filename))
            {
                throw(new ValidationException("Directory or file \"" + _filename + "\" doesn't exists.\nIf you're using a console application make sure the closing quote isn't escaped by a backslash (\\)."));
            }
        }
    }
}
